// =============================================================
// discovery test btw. server/client
// ---------------------------------------
// Author : Woo-Hyun Kim
// Email : woorung@icu.ac.kr or woorung@nhncorp.com
// Date : Oct. 8, 2007
// =============================================================
#include <iostream>

#include <sys/timeb.h>
#include <time.h>

#include "common_coord.h"
#include "property/CProperty.h"

#include "thread/CThread.h"
#include "service/CServiceRepository.h"

#include "CKeepAliveThread.h"

#include "CDiscoveryService.h"
#include "CDiscoveryProxy.h"


// user service
class CEchoService : public CService
{
public :
        CEchoService() : CService() {};
        CEchoService(char *name) : CService(name) {};
        CEchoService(const CEchoService &rhs) { this->setName((char *)rhs.getName()); };
        ~CEchoService() {};
        CEchoService *clone() const { return new CEchoService(*this); };
        CEchoService &operator=(const CEchoService &rhs) { this->setName((char *)rhs.getName()); return *this; };

        void run(cube__CObject *in, struct cube__invokeResponse &res) {
                res.out = in;
        };
};

void print_usage(char *prog)
{
	cout << "-------------------------------------------------------------------------" << endl;
	cout << "usage : " << prog << " [-c <config> or -p <port> -h <ip>] [-n <num>]" << endl;
	cout << "\t-c\t\tconfiguration file" << endl;
	cout << "\t-p\t\tport" << endl;
	cout << "\t-h\t\tip to connect" << endl;
	cout << "\t-n\t\tprocess num" << endl;
	cout << endl;
	cout << "example : " << endl;
	cout << "-- discovery server" << endl;
	cout << "\t" << prog << " -p 18088" << endl;
	cout << endl;
	cout << "-- discovery client" << endl;
	cout << "\t" << prog << " -h localhost -p 18088 -n 2" << endl;
	cout << "-------------------------------------------------------------------------" << endl;
}

int getRandom()
{ 
	srand(getpid());

	return rand();
}

// cube discovery
// cube discovery server
void cube_sd(int port)
{
	CServiceRepository *service_container = NULL;
	service_container = new CServiceRepository(port);

	// discovery
	CDiscoveryService discovery_service(CUBE_SERVICE_DISCOVERY);
	service_container->plugin(&discovery_service);

	cout << "===================================================" << endl;
	cout << COORD_VER << " server for discovery test" <<  endl;
	cout << endl;
	cout << "# available services" << endl;
	cout << discovery_service.getName() << endl;
	cout << "===================================================" << endl;

	service_container->run();

	delete service_container;
}

// cube itself
void cube_sp(char *host, int port)
{
	CServiceRepository *service_container = NULL;

	// set random port
	int random_port = (getRandom()%1000)+10080;
	service_container = new CServiceRepository(random_port);

	// echo
        CEchoService echo_service("sample.echo.service");
        service_container->plugin(&echo_service);

	cout << "===================================================" << endl;
	cout << COORD_VER << " client for discovery test" << endl;
	cout << "run on port = " << random_port << endl;
	cout << endl;
	cout << "# available services" << endl;
	cout << echo_service.getName() << endl;
	cout << "===================================================" << endl;

	// run keep-alive thread
	char thr_name[50];
	sprintf(thr_name,"thread-%d",getRandom()%5000);
	CKeepAliveThread keepalive(thr_name);
	keepalive.bind(host, port, service_container);
	keepalive.keepalive((getRandom()%15)+5);
	keepalive.start();

	// run cube itself
	service_container->run();
	
	delete service_container;
}

int main(int argc, char **argv)
{
	if(argc<2) {
		print_usage(argv[0]);
		exit(-1);
	}

	int opt;
	static char optstr[] = "cphn";

	char *conf= "coord.conf";
	int port = -1;
	int num = 1;
	string host = "";

	while((opt=getopt(argc,argv,optstr))!=-1) {
		switch(opt) {
			case 'c' :
			{
				conf = argv[optind++];
				CProperty config(conf);
				pair<string,string> kv;
                                kv.first = COORD_CONF_MASTER_IP_KEY;
                                host = config.get(kv).second;
                                kv.first = COORD_CONF_MASTER_PORT_KEY;
                                port = atoi((char *)config.get(kv).second.c_str());
				break;
			}
			case 'p' :
				port = atoi(argv[optind++]);
				break;
			case 'h' :
				host = argv[optind++];
				break;
			case 'n' :
				num = atoi(argv[optind++]);
				break;
			default :
				print_usage(argv[0]);
				exit(-1);
		}
	}

	if(port>0&&host=="") cube_sd(port);
	else {
		vector<pid_t> pids;
		for(int i=0;i<num;i++) {
			pid_t pid = fork();
			if(pid==0) { // child
				cube_sp((char *)host.c_str(), port);
				exit(1);
			}
			else if(pid>0) pids.push_back(pid); // parent
			else { // fork error
				sleep(1);
				i--;
			}
		}

		for(int i=0;i<num;i++) {
			waitpid(pids[i],NULL,0);
		}
	}

        return 1;
}
